package stardust.product.platform.webc;


import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.springframework.context.ApplicationContext;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import stardust.core.io.IOUtils;


/**
 * Webc配置类。 <br/>
 * 通过输入流读取符合stardust-webc.dtd定义的xml文件，在这里不做任何实际处理只做配置内容的识别和读入。
 * <p/>
 * @author ChenChang
 */
public class WebcConfig
{

  /**
   * 字符集配置参数名称。
   */
  public static final String CFG_CHARACTER_ENCODING = "CHARACTER_ENCODING";
  
  /**
   * 默认WEB资源响应渲染器。
   */
  public static final String CFG_DEFAULT_WEB_RENDERER = "DEFAULT_WEB_RENDERER";
  
  /**
   * VELOCITY参数配置。
   */
  public static final String CFG_VELOCITY_CONFIG = "VELOCITY_CONFIG";
  
  /**
   * 资源响应文件前导和后缀。
   */
  public static final String CFG_PATH_PREFIX = "PATH_PREFIX";
  public static final String CFG_PATH_SUFFIX = "PATH_SUFFIX";
  
  /**
   * 访问日志记录启用标志。
   */
  public static final String CFG_LOG = "LOG_ENABLED";
  
  private String contextName;

  private String containerType;

  private List<String> basePackage = new ArrayList<String>();

  private Map<String, String> parameter = new HashMap<String, String>();
  
  private List<String> callback = new ArrayList<String>();

  private Map<String, String> renderer = new HashMap<String, String>();
  
  private List<String> filterClass = new ArrayList<String>();

  /**
   * 通过一个符合stardust-webc.dtd规范的xml内容的输入流，从中读取Webc配置内容。
   * <p/>
   * @param inputStream
* 符合stardust-webc.dtd规范的xml内容的输入流，这是一个{@link java.io.InputStream}类型。
   */
  public WebcConfig(InputStream inputStream)
  {
    try
    {
      this.initialize(inputStream);
    }
    catch(ParserConfigurationException ex)
    {
      throw new WebcException("加载Webc配置失败。", ex);
    }
    catch(SAXException ex)
    {
      throw new WebcException("加载Webc配置失败。", ex);
    }
    catch(IOException ex)
    {
      throw new WebcException("加载Webc配置失败。", ex);
    }
  }

  /**
   * 获取上下文名称。<br/>
   * 通常情况下只有并且建议只使用一个上下文，这是为了在多上下文环境时用于区分不同的容器调用，例如：<br/>
   * security上下文处理/security/*的访问请求，shop上下文处理/shop/*的访问请求。
   */
  public String getContextName()
  {
    return this.contextName;
  }

  /**
   * 获取容器类型。<br/>
   * 1、new类型，指示容器对所有的资源类通过{@link Class.newInstance()}方式进行实例化，这是默认容器类型。<br/>
   * 2、spring类型，指示容器对所有的资源类通过spring容器获取实例，至少需要spring 3.0（含）以上版本。<br/>
   * 通过spring容器管理时通过{@link ApplicationContext#getBean()}
   * 实现，如果资源类未在spring中定义会自动注册后再获取。 3、其它类型，用于扩展容器功能，参数值是一个全限定类名，并且会通过{@link
   * Class.newInstance()}方式进行实例化后使用。
   */
  public String getContainerType()
  {
    return this.containerType;
  }

  public List<String> getBasePackage()
  {
    return Collections.unmodifiableList(this.basePackage);
  }

  public Map<String, String> getParameter()
  {
    return Collections.unmodifiableMap(this.parameter);
  }
  
  public List<String> getCallback()
  {
    return this.callback;
  }

  public Map<String, String> getRenderer()
  {
    return Collections.unmodifiableMap(this.renderer);
  }
  
  public List<String> getFilterClass()
  {
    return Collections.unmodifiableList(this.filterClass);
  }

  private void initialize(InputStream inputStream) throws ParserConfigurationException, SAXException, IOException
  {
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder db = dbf.newDocumentBuilder();
    // 禁止DTD验证，为不符合stardust-webc.dtd规范的xml内容提供容错性，但这可能发生不可预料的问题。
    db.setEntityResolver(new EntityResolver()
    {

      public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException
      {
        return new InputSource(IOUtils.BLANK_INPUTSTREAM);
      }

    });
    Document doc = db.parse(inputStream);

    Node webcNode;
    Node scanPackage;
    NodeList packageNodeList;
    Node configurationNode;
    NodeList parameterNodeList;
    Node pluginNode;
    NodeList rendererNodeList;
    Node filterNode;
    NodeList filterNodeList;

    webcNode = doc.getDocumentElement();
    if (webcNode.getNodeName().equals("webc"))
    {
      if (webcNode.getAttributes() != null && webcNode.getAttributes().getNamedItem("context-name") != null)
      {
        this.contextName = webcNode.getAttributes().getNamedItem("context-name").getTextContent();
      }
      if (webcNode.getAttributes() != null && webcNode.getAttributes().getNamedItem("container-type") != null)
      {
        this.containerType = webcNode.getAttributes().getNamedItem("container-type").getTextContent();
      }

      for (int i = 0; i < webcNode.getChildNodes().getLength(); i++)
      {
        if (webcNode.getChildNodes().item(i).getNodeType() != Node.ELEMENT_NODE)
        {
          continue;
        }
        else if (webcNode.getChildNodes().item(i).getNodeName().equals("scan-package"))
        {
          scanPackage = webcNode.getChildNodes().item(i);
          packageNodeList = scanPackage.getChildNodes();

          for (int j = 0; j < packageNodeList.getLength(); j++)
          {
            if (packageNodeList.item(j).getNodeType() != Node.ELEMENT_NODE)
            {
              continue;
            }

            if (packageNodeList.item(j).getNodeName().equals("package"))
            {
              this.basePackage.add(packageNodeList.item(j).getTextContent().trim());
            }
          }
        }
        else if (webcNode.getChildNodes().item(i).getNodeName().equals("configuration"))
        {
          configurationNode = webcNode.getChildNodes().item(i);
          parameterNodeList = configurationNode.getChildNodes();

          for (int j = 0; j < parameterNodeList.getLength(); j++)
          {
            if (parameterNodeList.item(j).getNodeType() != Node.ELEMENT_NODE)
            {
              continue;
            }

            if (parameterNodeList.item(j).getNodeName().equals("parameter"))
            {
              this.parameter.put(parameterNodeList.item(j).getAttributes().getNamedItem("name").getTextContent(), parameterNodeList.item(j).getTextContent().trim());
            }
          }
        }
        else if (webcNode.getChildNodes().item(i).getNodeName().equals("plugin"))
        {
          pluginNode = webcNode.getChildNodes().item(i);
          rendererNodeList = pluginNode.getChildNodes();

          for (int j = 0; j < rendererNodeList.getLength(); j++)
          {
            if (rendererNodeList.item(j).getNodeType() != Node.ELEMENT_NODE)
            {
              continue;
            }
            if (rendererNodeList.item(j).getNodeName().equals("callback"))
            {
              this.callback.add(rendererNodeList.item(j).getTextContent().trim());
            }
            else if (rendererNodeList.item(j).getNodeName().equals("renderer"))
            {
              this.renderer.put(rendererNodeList.item(j).getAttributes().getNamedItem("type").getTextContent(), rendererNodeList.item(j).getTextContent().trim());
            }
          }
        }
        else if (webcNode.getChildNodes().item(i).getNodeName().equals("filter"))
        {
          filterNode = webcNode.getChildNodes().item(i);
          filterNodeList = filterNode.getChildNodes();

          for (int j = 0; j < filterNodeList.getLength(); j++)
          {
            if (filterNodeList.item(j).getNodeType() != Node.ELEMENT_NODE)
            {
              continue;
            }

            if (filterNodeList.item(j).getNodeName().equals("filter-class"))
            {
              this.filterClass.add(filterNodeList.item(j).getTextContent().trim());
            }
          }
        }
      }
    }
  }

}
